Interpretation#

Load Data#

import pandas as pd

df = pd.read_csv('data/tag_df_final.csv')

Viewing the top 10 game aspects tagged with a non-neutral sentiment polarity reveals that ‘game’ has by far the highest frequency.

Given the lack of specific information this provides, it will be useful to preclude this token from subsequent charts.

(df[df['sentiment']!=0]
     .groupby('aspect',as_index=False)['description']
     .count().rename(columns={'description':'Count'})
     .sort_values(by='Count',ascending=False)
     .head(10)).reset_index(drop=True)
aspect Count
0 game 7968
1 campaign 1668
2 map 813
3 people 808
4 multiplayer 761
5 player 440
6 gun 439
7 gameplay 431
8 system 424
9 mode 352

Aspect Frequency#

Charting the most frequent aspects tagged with a non-neutral score, it is clear that players felt more strongly aboput the camapaign than other aspects of the game.

Campaign is the aspect of the game most frequently referred to in a positive context and the second most frquently cited negative aspect. However, the number of postivie references far outstrip all other aspects of game design in either sentiment polarity.

Notably, many aspects of game design appear to be quite divisive for the playerbase with several featuring in both sentiment lists.

#%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_style('whitegrid')

sns.catplot(data = (df[(df['sentiment']>0) & (df['aspect']!='game')]
                    .groupby('aspect')[['sentiment']]
                    .count()
                    .sort_values(by='sentiment',ascending=False)
                    .reset_index()
                    .head(20)),
            y='aspect',
            x='sentiment',
            kind='bar',
            palette = ['#88D8B0'],
            height = 6,
           aspect = 1.5)
plt.title('Positive Game Aspects',fontsize=14)
plt.tick_params(labelsize=12)
plt.ylabel('Descriptor',fontsize=12)
plt.xlabel('Count',fontsize=12)
plt.xticks(list(range(0,1401,100)))
plt.tight_layout()
plt.show();

sns.catplot(data = (df[(df['sentiment']<0) & (df['aspect']!='game')]
                    .groupby('aspect')[['sentiment']]
                    .count()
                    .sort_values(by='sentiment',ascending=False)
                    .reset_index()
                    .head(20)),
            y='aspect',
            x='sentiment',
            kind='bar',
            palette = ['#FF6F69'],
            height = 6,
           aspect = 1.5)
plt.title('Negative Game Aspects',fontsize=14)
plt.tick_params(labelsize=12)
plt.ylabel('Descriptor',fontsize=12)
plt.xlabel('Count',fontsize=12)
plt.xticks(list(range(0,1401,100)))
plt.tight_layout()
plt.show();
../_images/7_Interpretation_5_0.png ../_images/7_Interpretation_5_1.png
import numpy as np
import holoviews as hv
from holoviews import opts, dim
import colorcet as cc
hv.extension('bokeh')
hv.output(size=350)

# create list of aspects
aspect_list = list((df[(df['sentiment']!=0) & (df['aspect']!='game')]
                    .groupby('aspect')[['sentiment']]
                    .count()
                    .reset_index()
             ).loc[:,'aspect'])

# create df of aspects & descriptions with connection count
links = (df[(df['sentiment']!=0) & (df['description']!='other') & (df['aspect']!='game') & (df['aspect'].isin(aspect_list))]
     .groupby(by=['aspect','description'],as_index=False)['sentiment']
     .count().rename(columns={'sentiment':'Count'})
)
# restrict to connections >20
links = links[links['Count'] > 25]

# specify node names
nodes = list(set(links['aspect'].tolist() + links['description'].tolist()))
nodes = hv.Dataset(pd.DataFrame(nodes, columns = ['Token']))

# create chord diagram
chord = hv.Chord((links, nodes)).select(value=(5, None))
chord.opts(
    opts.Chord(labels = 'Token', label_text_font_size='12pt', 
               node_color='aspect', node_cmap=cc.cm.glasbey_light, node_size=10, 
               edge_color='aspect', edge_cmap=cc.cm.glasbey_light, 
               edge_alpha=0.9, edge_line_width=1)
)

label_data = chord.nodes.data label_data[‘rotation’] = np.arctan((label_data.y / label_data.x))

label_data[‘y’] = label_data[‘y’].apply(lambda x: x * 1.1) label_data[‘x’] = label_data[‘x’].apply(lambda x: x * 1.1)

labels = hv.Labels(label_data) labels.opts( opts.Labels(cmap=’magma’, text_font_size=’10pt’,padding=0.08, angle= dim(‘rotation’) * 1260/22 )) chord * labels

# create df counting all links between aspect and sentiment
counts_df = (df[(df['sentiment']!=0) & (df['aspect']!='game')]
    .groupby(by=['aspect','description'])[['sentiment']]
    .count()
    .rename(columns={'sentiment':'Count'})
    .sort_values(by ='Count', ascending =False)
    .reset_index()
)

# restrict to counts >10
counts_df = counts_df[counts_df['Count']>=15]

# create adjacency matrices for only tokens in counts_df
df_adj = df[df['aspect'].isin(set(df.aspect).intersection(set(counts_df.aspect))) & df['description'].isin(set(df.description).intersection(set(counts_df.description)))]
# adj1 (x,y)
adj1 = pd.crosstab(df_adj.description,(df_adj.aspect))
idx = adj1.columns.union(adj1.index)
adj1 = adj1.reindex(index=idx,columns=idx)
# adj2 (y,x)
adj2 = pd.crosstab(df_adj.aspect,df_adj.description)
idx = adj2.columns.union(adj2.index)
adj2 = adj2.reindex(index=idx,columns=idx)
# merge to replace make symetrical
adj = adj1.fillna(adj2)

links = adj.to_numpy()
nodes = list(adj.columns)

#%matplotlib
import mne
from mne.viz import circular_layout
from mne_connectivity.viz import plot_connectivity_circle

start, end = 45, 135
first_half = (np.linspace(start, end, len(nodes)//2) +90).astype(int)[::+1] %360
second_half = (np.linspace(start, end, len(nodes)//2) -90).astype(int)[::-1] %360
node_angles = np.array(list(first_half) + list(second_half))

fig, ax =  plt.subplots(figsize=(20, 20), facecolor='black',subplot_kw=dict(polar=True))
plot_connectivity_circle(links, nodes, interactive= True, ax=ax)
fig.tight_layout()
../_images/7_Interpretation_8_0.png
#list(set(counts_df.aspect))
counts_df = (df[(df['sentiment']!=0) & (df['aspect']!='game')]
    .groupby(by=['aspect','description'])[['sentiment']]
    .count()
    .rename(columns={'sentiment':'Count'})
    .sort_values(by ='Count', ascending =False)
    .reset_index()
)
counts_df[counts_df['Count']>15].head(45)
aspect description Count
0 campaign good 176
1 campaign great 114
2 warfare modern 79
3 campaign amazing 76
4 map bad 68
5 campaign fun 66
6 multiplayer fun 64
7 map good 55
8 cod good 54
9 gun good 53
10 boy free 52
11 multiplayer great 51
12 gameplay good 48
13 multiplayer good 47
14 people other 47
15 people most 44
16 map terrible 43
17 people good 42
18 thing good 41
19 campaign worth 41
20 campaign awesome 40
21 gameplay fun 35
22 request fucking 34
23 campaign really good 34
24 campaign pretty good 33
25 call good 31
26 people more 31
27 campaign bad 31
28 graphic good 30
29 gameplay great 29
30 cod old 29
31 system new 28
32 story good 27
33 cod bad 26
34 gun great 26
35 call bad 26
36 gun bad 26
37 player single 25
38 campaign solid 25
39 people bad 25
40 campaign other 24
41 mode good 24
42 player other 24
43 warne free 23
44 campaign decent 23